﻿using System;
using System.Reflection;
using Microsoft.Xna.Framework;
using WindowsGame.Builder;
using WindowsGame.Components;
using WindowsGame.Enums;
using WindowsGame.GameObjects;
using WindowsGame.Manager;
using WindowsGame.Static;
using WindowsGame.Library.Data;
using WindowsGame.Library.IoC;
using WindowsGame.Library.ProxyObjects;
using WindowsGame.Tests.Data;
using NUnit.Framework;

namespace WindowsGame.Tests.Builder
{
	[TestFixture]
	public class GameObjectBuilderTests
	{
		// System under test.
		private GameObjectBuilder gameObjectBuilder;

		[TestFixtureSetUp]
		public void TestFixtureSetUp()
		{
			IoCContainer.Initialize<IFileProxy, TestFileProxy>();
			IoCContainer.Initialize<IFileManager, FileManager>();

			IoCContainer.Initialize<GameObjectBuilder, GameObjectBuilder>();
			IoCContainer.Initialize<IGameObjectManager, GameObjectManager>();
			IoCContainer.Initialize<ProxyObjectBuilder, ProxyObjectBuilder>();
			IoCContainer.Initialize<IProxyObjectManager, ProxyObjectManager>();
		}

		[SetUp]
		public void SetUp()
		{
			gameObjectBuilder = IoCContainer.Resolve<GameObjectBuilder>();
		}

		[Test]
		public void BuildGameObjectSpaceshipComponentTest()
		{
			String path = GetPath();
			String[] xmlFiles = new[] { "Spaceships.xml" };
			gameObjectBuilder.ProxyObjectBuilder.BuildProxyObjects(path, xmlFiles);

			String assetName = "WedgeTwoSkinOne";
			Spaceship spaceship = gameObjectBuilder.BuildGameObject<Spaceship>(assetName);

			var component = spaceship.GetComponent<ModelComponent>();
			Assert.AreEqual(ShipClass.WedgeTwo, component.ShipClass);
			Assert.AreEqual("p2_wedge", component.ModelName);
			Assert.AreEqual("wedge_p2_diff_v1", component.TextureName);
		}

		[Test]
		public void BuildGameObjectWeaponComponentTest()
		{
			String path = GetPath();
			String[] xmlFiles = new[] { "Weapons.xml" };
			gameObjectBuilder.ProxyObjectBuilder.BuildProxyObjects(path, xmlFiles);

			String assetName = "RocketOne";
			Weapon weapon = gameObjectBuilder.BuildGameObject<Weapon>(assetName);

			var component = weapon.GetComponent<WeaponComponent>();
			Assert.AreEqual(1, component.Burst);
			Assert.AreEqual(3000, component.Cost);
			Assert.AreEqual(2, component.Maximum);
		}

		[Test]
		public void BuildGameObjectSpaceshipPropertiesTest()
		{
			String path = GetPath();
			String[] xmlFiles = new[] { "Spaceships.xml" };
			gameObjectBuilder.ProxyObjectBuilder.BuildProxyObjects(path, xmlFiles);

			String assetName = "WedgeTwoSkinOne";
			Spaceship spaceship = gameObjectBuilder.BuildGameObject<Spaceship>(assetName);

			Console.WriteLine("SPACESHIP: {0}", assetName);
			foreach (var component in spaceship.Components.Values)
			{
				Console.WriteLine(" Component: {0}", component.GetType().Name);
				PropertyInfo[] properties = component.GetType().GetProperties();
				foreach (PropertyInfo property in properties)
				{
					String name = property.Name;
					if (String.Compare(name, "Owner") == 0)
					{
						continue;
					}

					Object obj = property.GetValue(component, null);
					Console.WriteLine("  Key: {0}  Value: {1}", name, obj);
				}
			}
		}

		[Test]
		public void BuildGameObjectWeaponPropertiesTest()
		{
			String path = GetPath();
			String[] xmlFiles = new[] { "Weapons.xml" };
			gameObjectBuilder.ProxyObjectBuilder.BuildProxyObjects(path, xmlFiles);

			String assetName = "RocketOne";
			Weapon weapon = gameObjectBuilder.BuildGameObject<Weapon>(assetName);

			Console.WriteLine("SPACESHIP: {0}", assetName);
			foreach (var component in weapon.Components.Values)
			{
				Console.WriteLine(" Component: {0}", component.GetType().Name);
				PropertyInfo[] properties = component.GetType().GetProperties();
				foreach (PropertyInfo property in properties)
				{
					String name = property.Name;
					if (String.Compare(name, "Owner") == 0)
					{
						continue;
					}

					Object obj = property.GetValue(component, null);
					Console.WriteLine("  Key: {0}  Value: {1}", name, obj);
				}
			}
		}

		[Test]
		public void BuildGameObjectInvalidTest()
		{
			String path = GetPath();
			String[] xmlFiles = new[] { "Spaceships.xml" };
			gameObjectBuilder.ProxyObjectBuilder.BuildProxyObjects(path, xmlFiles);

			String assetName = "DummyObject";
			Spaceship spaceship = gameObjectBuilder.BuildGameObject<Spaceship>(assetName);

			Assert.IsNull(spaceship, assetName + " is null");
		}

		[Test]
		public void BuildGameObjectSpaceshipTest()
		{
			var comp1 = new ProxyComponent("ModelComponent_100");
			comp1.AddProperty("id", "100");
			comp1.AddProperty("shipClass", "SaucerOne");
			comp1.AddProperty("modelName", "p1_saucer");
			comp1.AddProperty("textureName", "saucer_p1_diff_v1");

			var comp2 = new ProxyComponent("PhysicsComponent_200");
			comp2.AddProperty("id", "200");
			comp2.AddProperty("modelScale", "0.05");
			comp2.AddProperty("rotateScale", "0.02");
			comp2.AddProperty("position", "-200,0,0");
			comp2.AddProperty("rotation", "90,0,270");
			comp2.AddProperty("boundLeft", "-400");
			comp2.AddProperty("boundRight", "400");
			comp2.AddProperty("boundTop", "-250");
			comp2.AddProperty("boundBottom", "250");

			var comp3 = new ProxyComponent("ThrustComponent_300");
			comp3.AddProperty("id", "300");
			comp3.AddProperty("textureName", "thrust_stripSmall");
			comp3.AddProperty("thrustFrame", "12.0");
			comp3.AddProperty("thrustConstant", "12");
			comp3.AddProperty("thrustMaximum", "29");
			comp3.AddProperty("thrustFactor", "1.0");
			comp3.AddProperty("thrustPower", "100");
			comp3.AddProperty("bleedVelocity", "0.3");

			ProxyObject proxyObject = new ProxyObject("SaucerOneSkinOne");
			proxyObject.AddComponent(comp1);
			proxyObject.AddComponent(comp2);
			proxyObject.AddComponent(comp3);

			GameObject gameObject = gameObjectBuilder.BuildGameObject(proxyObject);
			Assert.AreEqual(3, gameObject.Components.Count);

			var component = gameObject.GetComponent<ModelComponent>();
			Assert.AreEqual(100, component.ID);
		}

		[Test]
		public void BuildGameObjectWeaponTest()
		{
			var comp1 = new ProxyComponent("ModelComponent_100");
			comp1.AddProperty("id", "100");
			comp1.AddProperty("modelName", "p1_pea");
			comp1.AddProperty("textureName", "pea_proj");

			var comp2 = new ProxyComponent("PhysicsComponent_200");
			comp2.AddProperty("id", "200");
			comp2.AddProperty("modelScale", "0.02");
			comp2.AddProperty("rotateScale", "0.02");
			comp2.AddProperty("rotation", "90,0,0");
			comp2.AddProperty("boundLeft", "-400");
			comp2.AddProperty("boundRight", "400");
			comp2.AddProperty("boundTop", "-250");
			comp2.AddProperty("boundBottom", "250");

			var comp3 = new ProxyComponent("WeaponComponent_400");
			comp3.AddProperty("id", "400");
			comp3.AddProperty("cost", "0");
			comp3.AddProperty("lifetime", "3");
			comp3.AddProperty("maximum", "5");
			comp3.AddProperty("burst", "1");
			comp3.AddProperty("acceleration", "0");
			comp3.AddProperty("damage", "1");

			ProxyObject proxyObject = new ProxyObject("SaucerOneSkinOne");
			proxyObject.AddComponent(comp1);
			proxyObject.AddComponent(comp2);
			proxyObject.AddComponent(comp3);

			GameObject gameObject = gameObjectBuilder.BuildGameObject(proxyObject);
			Assert.AreEqual(3, gameObject.Components.Count);

			var component = gameObject.GetComponent<WeaponComponent>();
			Assert.AreEqual(400, component.ID);
		}

		private static String GetPath()
		{
			String root = @"..\..\..\..\WindowsGame\WindowsGame\bin\x86\Debug\";
			String path = String.Format("{0}/{1}/{2}", Constants.ROOT_DIRECTORY, Constants.DATA_DIRECTORY, String.Empty);

			return String.Format("{0}{1}", root, path);
		}

		[TestFixtureTearDown]
		public void TestFixtureTearDown()
		{
			IoCContainer.Release();
		}
	}
}
